package com.zxk.demo.mybatis;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zxk.demo.entity.BaseDomain;
import org.apache.ibatis.binding.MapperMethod;
import org.springframework.beans.BeanUtils;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;


public class CustomBaseServiceImpl<M extends CustomBaseMapper<T>, T extends BaseDomain> extends ServiceImpl<M, T> {
    /**
     * 删除并更新内容 根据ID
     *
     * @param entity 对象
     * @return
     */
    public int deleteAndUpdateById(T entity) {
        return baseMapper.deleteAndUpdateById(entity);
    }


    public List<Long> selectByTenantIds(List<Long> tenantIds) {
        QueryWrapper queryWrapper = new QueryWrapper<>();
        queryWrapper.select("id");
        queryWrapper.in(!tenantIds.contains(0L), "tenant_id", tenantIds);
        return listObjs(queryWrapper, id -> Long.valueOf(id.toString()));
    }


    /**
     * 删除并更新内容 根据ID 批量更新
     *
     * @param entityList 实体对象集合
     */
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteAndUpdateBatchById(Collection<T> entityList) {
        String sqlStatement = getSqlStatement(mapperClass, CustomSqlMethod.DELETE_AND_UPDATE_BY_ID);
        return executeBatch(entityList, DEFAULT_BATCH_SIZE, (sqlSession, entity) -> {
            MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
            param.put(Constants.ENTITY, entity);
            sqlSession.update(sqlStatement, param);
        });
    }

    /**
     * 获取mapperStatementId
     *
     * @param sqlMethod 方法名
     * @return 命名id
     * @since 3.4.0
     */
    public static String getSqlStatement(Class<?> mapper, CustomSqlMethod sqlMethod) {
        return mapper.getName() + StringPool.DOT + sqlMethod.getMethod();
    }

    /**
     * 通过id查询详情
     *
     * @param id
     * @param <R>
     * @return
     */
    protected <R> R getById(Long id, Class<R> clazz) throws InstantiationException, IllegalAccessException {
        T entity = super.getById(id);
        return convert2DTO(entity, clazz);
    }

    /**
     * DB类转换成DTO
     *
     * @param t
     * @param clazz
     * @return
     */
    protected <R> R convert2DTO(T t, Class<R> clazz) throws InstantiationException, IllegalAccessException {
        R r = clazz.newInstance();
        BeanUtils.copyProperties(t, r);
        return r;
    }

    /**
     * DB类转换成DTO
     *
     * @param list
     * @param clazz
     * @return
     */
    protected <R> List<R> convertList2DTO(List<T> list, Class<R> clazz) {
        return list.stream().map(t -> {
            try {
                return convert2DTO(t, clazz);
            } catch (InstantiationException e) {
                throw new RuntimeException(e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }).collect(Collectors.toList());
    }

    /**
     * DB类转换成DTO
     *
     * @param list
     * @param clazz
     * @return
     */
    protected <R> List<R> convert2DTOBatch(List<T> list, Class<R> clazz) {
        return list.stream()
                .map(t -> {
                    try {
                        R r = clazz.newInstance();
                        BeanUtils.copyProperties(t, r);
                        return r;
                    } catch (Exception e) {
                        throw new RuntimeException("实例化对象失败，Class=" + clazz.getName());
                    }
                })
                .collect(Collectors.toList());
    }

    /**
     * DTO转换成DB类
     *
     * @param r
     * @return
     */
    protected <R> T convert2DO(R r) {
        //获得当前类带有泛型类型的父类
        ParameterizedType ptClass = (ParameterizedType) this.getClass().getGenericSuperclass();
        //获得运行期的泛型类型
        Class<T> clazz = (Class<T>) ptClass.getActualTypeArguments()[1];
        try {
            T t = clazz.newInstance();
            BeanUtils.copyProperties(r, t);
            return t;
        } catch (Exception e) {
            throw new RuntimeException("实例化对象失败，Class=" + clazz.getName());
        }
    }


}
